import os.path
import sys
import tempfile
from math import floor

import SimpleITK as sitk
import pydicom

from ExtendScripts.DicomAutoConvert.DicomQualityAdapter import DicomQualityAdapter


class DicomDataCheck:
    """
        sitk_check_frame_loss: 用于帧丢检测

    """
    @staticmethod
    def sitk_data_useable_check(root: str, dcm_list: list):
        reader = sitk.ImageSeriesReader()
        series_IDs = reader.GetGDCMSeriesIDs(root)
        serial_quality_list = []
        for ser_id in series_IDs:
            series_file_names = reader.GetGDCMSeriesFileNames(root, ser_id)
            reader.SetFileNames(series_file_names)
            dicom_series = reader.Execute()

            if sys.platform.startswith("win"):
                temp_nrrd = tempfile.NamedTemporaryFile(suffix=".nrrd")
                temp_png = tempfile.NamedTemporaryFile(suffix=".png")
                temp_nrrd.close()
                temp_png.close()
            else:
                temp_nrrd = tempfile.NamedTemporaryFile(suffix=".nrrd", delete=True)
                temp_png = tempfile.NamedTemporaryFile(suffix=".png", delete=True)

            sitk.WriteImage(dicom_series, temp_nrrd.name)
            DicomQualityAdapter.convert_nrrd_to_png(temp_nrrd.name, temp_png.name)
            cls_result = DicomQualityAdapter.read_classify_result(temp_png.name)
            serial_quality_list.append((ser_id, cls_result))

            if sys.platform.startswith("win"):
                os.remove(temp_nrrd.name)
                os.remove(temp_png.name)

        most_match_data_index = -1
        for each_record in range(len(serial_quality_list)):
            if serial_quality_list[each_record][1] == 2:
                most_match_data_index = each_record
                break
        if most_match_data_index == -1:
            for each_record in range(len(serial_quality_list)):
                if serial_quality_list[each_record][1] == 1:
                    most_match_data_index = each_record
                    break

        return serial_quality_list, most_match_data_index

    @staticmethod
    def sitk_windows_loss(root: str):
        reader = sitk.ImageSeriesReader()
        series_IDs = reader.GetGDCMSeriesIDs(root)
        if not series_IDs:
            # print("未找到有效series")
            return False
        # print(series_IDs)
        series_file_names = reader.GetGDCMSeriesFileNames(root, series_IDs[0])
        reader.SetFileNames(series_file_names)
        try:
            # TODO: 这里添加拦截c++端的warning，如果出现waring说明读取有问题
            image3d = reader.Execute()
            return True
        except Exception as e :
            # print("窗丢失")
            return False

    @staticmethod
    def sitk_check_frame_loss_v3(root: str, dcm_list: list, default_tolerance: int = 2):
        """sitk_check_frame_loss_v3
            此函数并非方法判断接口，适用于实际数据情况复杂而设置的封装函数

            注意受此影响，会微调v1, v2相关代码
        """
        reader = sitk.ImageSeriesReader()
        series_IDs = reader.GetGDCMSeriesIDs(root)
        has_useable_data = False
        for ser_id in series_IDs:
            series_file_names = reader.GetGDCMSeriesFileNames(root, ser_id)
            # TODO: 如果序列长度太短，通常字段缺失不进行后续判断
            if len(series_file_names) < 10:
                continue

            has_useable_data = True
            # 使用物理参数计算帧丢失问题
            if not DicomDataCheck.sitk_check_frame_loss_v2(root, series_file_names, default_tolerance):
                return False
            # 使用实例id判断跳帧，重复帧的问题
            # TODO: 由于中间丢帧，sitk不会报错，但是slicer无法转换，所以将此步骤设定为1
            # if not DicomDataCheck.sitk_check_frame_loss_v1(root, series_file_names, default_tolerance):
            if not DicomDataCheck.sitk_check_frame_loss_v1(root, series_file_names, 1):
                return False

        return True and has_useable_data

    @staticmethod
    def sitk_check_frame_loss_v2(root: str, dcm_list: list, default_tolerance: int = 3):
        """sitk_check_frame_loss_v2
            通过判断整体的帧数丢失，来返回判断状态
        """
        ds = pydicom.dcmread(dcm_list[0])
        # SpacingBetweenSlices
        if (0x0018, 0x0088) in ds:
            try:
                space = int(ds.SpacingBetweenSlices)
            except Exception as e:
                raise RuntimeError("读取到SpacingBetweenSlices但数值为空")
        # SliceThickness
        elif (0x0018, 0x0050) in ds:
            try:
                space = int(ds.SliceThickness)
            except Exception as e:
                raise RuntimeError("读取到SliceThickness但数值为空")
        else:
            raise RuntimeError("无可用于计算切片数量的物理参数")

        if space == 0:
            raise RuntimeError("用于计算切片数量的物理量为0")

        dcm_tags = [sitk.ReadImage(f) for f in dcm_list]
        z_position = sorted([img.GetOrigin()[2] for img in dcm_tags])
        total_number = floor(abs(z_position[-1] - z_position[0]) / abs(space)) + 1

        if abs(int(total_number) - len(dcm_list)) >= default_tolerance:
            return False
        return True


    @staticmethod
    def sitk_check_frame_loss_v1(root: str, dcm_list: list, default_tolerance: int = 3):
        """sitk_check_frame_loss_v1
            此函数可以用于检测同一个文件夹中serial_id相同, 但存在多个副本的情况

            TODO: 未完成的代码，主要用于判断中间数据的缺失数量，是否达到阈值

            1. 判断连续帧丢失
            2. 跳帧次数太高
        """
        instance_num = [pydicom.dcmread(each_dcm).InstanceNumber for each_dcm in dcm_list]
        instance_num.sort()
        # print("Instance_number", instance_num)

        now_number = 1
        continue_loss_number = 0
        loss_occur_count = 0
        for each_id in instance_num:
            each_id = int(each_id)
            if each_id == now_number:
                continue_loss_number = 0
            else:
                continue_loss_number += (each_id - now_number)
                now_number = each_id

                loss_occur_count += 1
                if loss_occur_count >= default_tolerance:
                    return False  # 跳帧次数太多
                if continue_loss_number >= default_tolerance:
                    return False  # 单次跳帧数量太多

            now_number += 1
        return True




